<template>
	<view class="chart">
		<!-- 图表容器需要明确的尺寸 -->
		<view class="tools">
			<view class="period" :period="currentPeriod" :change:period="renderjs.updatePeriod">
				<view @click="onPeriodChange(k)" class="col" v-for="(v, k) in preferencePeriods" :key="k"><view :class="{block: k == currentPeriod}">{{v}}</view></view>
			</view>
			<view class="indic">
				<view class="col">
					<view class="more" @click="showPeriodPanel = true">
						<text>{{i18n[lang].more}}</text>
						<u-icon :name="showPeriodPanel ? 'arrow-up-fill' : 'arrow-down-fill'" size="18"></u-icon>
					</view>
				</view>
				<view class="col"><u-icon name="setting" size="34" @click="showIndicPanel = true"></u-icon></view>
			</view>
		</view>
		<view class="chart" id="chart" :lang="lang" :change:lang="renderjs.updateLang" :symbol="symbol" :change:symbol="renderjs.updateSymbol"
			:style="{ width: width, height: height + 'rpx' }"></view>
		
		<view class="indicator">
			<scroll-view scroll-x="true">
				<view class="tabs" :main="currentMainIndic" :sub="currentSubIndic" :change:main="renderjs.updateMainIndic" :change:sub="renderjs.updateSubIndic">
					<view @click="onIndicChange(item)" v-for="(item, index) in mainIndicators"  class="scroll-view-item" :class="{'active': currentMainIndic.includes(item.name)}">{{item.name}}</view>
					<view class="scroll-view-item">|</view>
					<view @click="onIndicChange(item)" v-for="(item, index) in subIndicators" class="scroll-view-item" :class="{'active': currentSubIndic.includes(item.name)}">{{item.name}}</view>
				</view>
			</scroll-view>
		</view>
		
		<u-popup v-model="showPeriodPanel" border-radius="20" mode="bottom" height="500rpx">
			<view class="popup-period">
				<!-- <view class="t1">周期偏好</view>
				<view class="grid">
				</view> -->
				<view class="t1">{{i18n[lang].allPeriod}}</view>
				<view class="grid">
					<view @click="onPeriodChange(k)" :class="{'active': k == currentPeriod}" class="col" v-for="(v, k) in periods" :key="k">{{v}}</view>
				</view>
			</view>
		</u-popup>
		
		<u-popup v-model="showIndicPanel" border-radius="20" mode="bottom" height="600rpx">
			<view class="popup-period">
				<view class="t1">{{i18n[lang].mainIndic}}</view>
				<view class="grid">
					<view class="col" :class="{'active': currentMainIndic.includes(item.name)}" @click="onIndicChange(item)" v-for="item in mainIndicators" :key="item.name">{{item.name}}</view>
				</view>
				<view class="t1">{{i18n[lang].subIndic}}</view>
				<view class="grid">
					<view class="col" :class="{'active': currentSubIndic.includes(item.name)}" @click="onIndicChange(item)" v-for="item in subIndicators" :key="item.name">{{item.name}}</view>
				</view>
			</view>
		</u-popup>
	</view>
</template>

<script>
	let i18n = require('./lang.js')
	export default {
		props: {
			data: {
				type: Object,
				default: () => ({})
			},
			width: {
				type: String,
				default: '100%'
			},
			height: {
				type: Number,
				default: 500
			},
			symbol: {
				type: String,
				default: ''
			},
			lang: {
				type: String,
				default: 'zh-CN'
			},
			theme: {
				type: String,
				default: 'dark' // light/dark
			}
		},
		data(){
			return {
				i18n: i18n,
				showPeriodPanel: false,
				showIndicPanel: false,
				currentPeriod: '1_day',
				currentMainIndic: ['MA'],
				currentSubIndic: ['VOL'],
				preferencePeriods: {
					'line': `${i18n[this.lang].line}`,
					'5_minute': `5${i18n[this.lang].min}`,
					'15_minute': `15${i18n[this.lang].min}`,
					'60_minute': `1${i18n[this.lang].hour}`,
					'1_day': `1${i18n[this.lang].day}`
				},
				periods: {
					'1_minute': `1${i18n[this.lang].min}`,
					'5_minute': `5${i18n[this.lang].min}`,
					'15_minute': `15${i18n[this.lang].min}`,
					'30_minute': `30${i18n[this.lang].min}`,
					'60_minute': `1${i18n[this.lang].hour}`,
					'1_day': `1${i18n[this.lang].day}`,
					'1_week': `1${i18n[this.lang].week}`,
					'1_month': `1${i18n[this.lang].month}`
				},
				mainIndicators: [
					{ name: 'MA', type: 0 }, 
					{ name: 'EMA', type: 0 }, 
					{ name: 'BOLL', type: 0 }, 
					{ name: 'SAR', type: 0 },
				],
				subIndicators: [
					{ name: 'VOL', type: 1 },
					{ name: 'MACD', type: 1 }, 
					{ name: 'KDJ', type: 1 }, 
					{ name: 'BOLL', type: 1 }, 
					{ name: 'RSI', type: 1 }, 
					{ name: 'BIAS', type: 1 }, 
					{ name: 'BRAR', type: 1 }, 
					{ name: 'CCI', type: 1 }, 
					{ name: 'DMI', type: 1 }, 
					{ name: 'CR', type: 1 }, 
					{ name: 'PSY', type: 1 }, 
					{ name: 'DMA', type: 1 }, 
					{ name: 'TRIX', type: 1 }, 
					{ name: 'OBV', type: 1 }, 
					{ name: 'VR', type: 1 }, 
					{ name: 'WR', type: 1 }, 
					{ name: 'MTM', type: 1 }, 
					{ name: 'EMV', type: 1 }, 
					{ name: 'SAR', type: 1 }, 
					{ name: 'AO', type: 1 }, 
					{ name: 'ROC', type: 1 }
				]
			};
		},
		methods: {
			onPeriodChange(period){
				this.currentPeriod = period;
			},
			onIndicChange(item) {
				if(item.type == 0){
					const index = this.currentMainIndic.indexOf(item.name);
					if(index > -1){
						this.currentMainIndic.splice(index, 1);
					} else {
						this.currentMainIndic.push(item.name);
					}
				} else {
					const index = this.currentSubIndic.indexOf(item.name);
					if(index > -1){
						this.currentSubIndic.splice(index, 1);
					} else {
						if(this.currentSubIndic.length == 2){
							this.currentSubIndic.splice(1, 1);
						}
						this.currentSubIndic.push(item.name);
					}
				}
			},
			// 暴露给父组件的方法
			getChartInstance() {
				
				return this.$refs.renderjs.getChartInstance()
			}
		}
	}
</script>

<script ref="renderjs" module="renderjs" lang="renderjs">
	import WebSocketManager from './websocket';
	import global from '@/utils/global'
	import {
		init,
		dispose,
		registerLocale,
		registerYAxis,
		utils,
		getSupportedIndicators
	} from 'klinecharts'
	//import styles from '@/static/js/klineConfig.js';
	
	registerLocale('zh-TW', {
	  time: '時間：',
	  open: '開：',
	  high: '高：',
	  low: '低：',
	  close: '收：',
	  volume: '量：',
	  second: '秒',
	  minute: '分鐘',
	  hour: '小時',
	  day: '天',
	  week: '週',
	  month: '月',
	  year: '年'
	})
	export default {
		data() {
			return {
				chart: null,
				mainIndicMap: [],
				subIndicMap: [],
				socket: null,
				renderjsLang: ''
			}
		},
		watch: {
		    chart(newVal, oldVal) {
			  this.$nextTick(() => {
			  	this.chart.setLocale(this.renderjsLang);
			  });
		    }
		},
		mounted() {
			this.socket = new WebSocketManager(`${global.WS_URL}?type=kline`, {
			  heartbeatInterval: 5000, // 10秒心跳
			  reconnectInterval: 5000,  // 5秒重连
			});
			
			// 监听连接状态
			this.socket.on('open', () => {
			  console.log('连接已建立');
			  // setInterval(() => {
			  // 		  this.socket.send(JSON.stringify({"ping": new Date().getTime()}));
			  // }, 3000)
			});
			
			this.socket.on('close', () => {
			  console.log('连接已关闭');
			});
			
		},
		destroyed() {
			if (this.socket) {
			  this.socket.close();
			}
			this.disposeChart()
		},
		methods: {
			// 暴露给外部的方法
			getChartInstance() {
				return this.chart
			},
			generateSampleData() {
				
			  // 生成示例数据
			  const data = []
			  const now = new Date().getTime()
			  const oneDay = 24 * 60 * 60 * 1000
			  
			  for (let i = 30; i >= 0; i--) {
			    const open = 100 + Math.random() * 20
			    const close = open + (Math.random() - 0.5) * 10
			    const high = Math.max(open, close) + Math.random() * 5
			    const low = Math.min(open, close) - Math.random() * 5
			    
			    data.push({
			      timestamp: now - i * oneDay,
			      open: parseFloat(open.toFixed(2)),
			      high: parseFloat(high.toFixed(2)),
			      low: parseFloat(low.toFixed(2)),
			      close: parseFloat(close.toFixed(2)),
			      volume: parseFloat((Math.random() * 10000).toFixed(2))
			    })
			  }
			  
			  return data
			},
			updateSymbol(symbol){
				this.$nextTick(() => {
					 if (!this.chart) {
						console.log('init symbol: ', symbol);
						this.initChart(symbol)
					 } else {
						 console.log('update symbol: ', symbol);
						 this.chart.setSymbol({ ticker: symbol })
					 }
				});
			},
			updatePeriod(period){
				if(!this.chart){
					return;
				}
				//console.log('period: ', period);
				
				
				//console.log('span: ', parseInt(periods[0]), ',  type: ', periods[1])
				
				if(period == 'line'){
					this.chart.setPeriod({ span: 1, type: 'minute' });
					this.chart.setStyles({
						candle: {
							type: 'area'
						}
					});
				} else {
					const periods = period.split('_');
					this.chart.setPeriod({ span: parseInt(periods[0]), type: periods[1] });
					this.chart.setStyles({
						candle: {
							type: 'candle_solid'
						}
					});
				}
			},
			updateMainIndic(data){
				this.$nextTick(() => {
					if(this.mainIndicMap.length > data.length){
						const deleteIndics = this.mainIndicMap.reduce((acc, item) => {
						  if (!data.includes(item)) {
						    acc.push(item);
						  }
						  return acc;
						}, []);
						for(let indic of deleteIndics){
							this.chart.removeIndicator({ name: indic });
							let index = this.mainIndicMap.indexOf(indic);
							this.mainIndicMap.splice(index, 1);
						}
					}
					for(let indic of data){
						if(this.mainIndicMap.includes(indic)){
							continue;
						}
						this.mainIndicMap.push(indic);
						this.chart.createIndicator(indic, true, {
							id: 'candle_pane'
						})
						if(indic == 'MA'){
							this.chart.overrideIndicator({
							  name: 'MA',
							  shouldOhlc: false,
							  precision: 2,
							  calcParams: [5, 10, 20],
							  styles: {
							    lines: [
							      { color: '#8fd3e8' },
							      { color: '#edafda' },
								  { color: '#8b86ea' }
							    ]
							  }
							})
						}
					}
				});
			},
			updateSubIndic(data){
				this.$nextTick(() => {
					const arrayEqualsIgnoreOrder = (a, b) => {
					  return a.length === b.length && 
					         a.every(val => b.includes(val)) && 
					         b.every(val => a.includes(val));
					};
					if(!arrayEqualsIgnoreOrder(this.subIndicMap, data)){
						const deleteIndics = this.subIndicMap.reduce((acc, item) => {
						  if (!data.includes(item)) {
						    acc.push(item);
						  }
						  return acc;
						}, []);
						for(let indic of deleteIndics){
							this.chart.removeIndicator({ name: indic });
							let index = this.subIndicMap.indexOf(indic);
							this.subIndicMap.splice(index, 1);
						}
					}
					for(let indic of data){
						if(this.subIndicMap.includes(indic)){
							continue;
						}
						this.subIndicMap.push(indic);
						this.chart.createIndicator(indic, false, {
							id: indic + '_pane',
							height: 70,
							axis: {
							  inside: true
							}
						})
						if(indic == 'VOL'){
							this.chart.overrideIndicator({
							  name: 'VOL',
							  shouldOhlc: false,
							  precision: 2,
							  calcParams: [5, 10, 20],
							  styles: {
							    lines: [
							      { color: '#8fd3e8' },
							      { color: '#edafda' },
								  { color: '#8b86ea' }
							    ]
							  }
							})
						}
					}
				});
			},
			updateLang(lang){
				this.renderjsLang = lang;
				
			},
			initChart(symbol) {
				if (!this.$el) {
					return;
				}
				const container = this.$el.querySelector(`#chart`)
				if (!container) return
				//console.log("initChart: ",this.data.data);
				this.chart = init(container, {
					// 默认配置
					locale: 'zh-CN',
					timezone: 'Asia/Shanghai',
					layout: [
					    {
					      type: "candle",
					      options: {
					        axis: {
					          inside: true,
					        },
					      },
					    },
						{
						  type: "xAxis",
						  options: {
						    axis: {
						      inside: true,
						    },
						  },
						},
					],
					styles: {
						grid: {
							show: true,
							horizontal: {
								show: true,
								size: 1,
								color: '#EDEDED',
								style: 'dashed',
								dashedValue: [2]
							},
							vertical: {
								show: true,
								size: 1,
								color: '#EDEDED',
								style: 'dashed',
								dashedValue: [2]
							}
						},
						candle: {
							type: 'candle_solid',
							tooltip: {
							  showType: 'rect',
							  showRule: 'follow_cross',
							}
						},
						xAxis: {
							show: true,
							size: 'auto',
							axisLine: {
							  show: false,
							  color: '#888888',
							  size: 1
							},
							tickText: {
							  show: true,
							  color: '#D9D9D9',
							  family: 'Helvetica Neue',
							  weight: 'normal',
							  size: 10,
							  marginStart: 4,
							  marginEnd: 4
							},
							tickLine: {
							  show: false,
							  size: 1,
							  length: 3,
							  color: '#888888'
							}
						  },
						  yAxis: {
							  show: true,
							  size: 'auto',
							  axisLine: {
								show: false,
								color: '#1d9a2c',
								size: 1
							  },
							  tickText: {
								show: true,
								color: '#D9D9D9',
								family: 'Helvetica Neue',
								weight: 'normal',
								size: 10,
								marginStart: 4,
								marginEnd: 4
							  },
							  tickLine: {
								show: false,
								size: 1,
								length: 3,
								color: '#888888'
							  }
							},
							separator: {
								size: 1,
								color: '#DDDDDD',
								fill: true
							},
							indicator: {
								tooltip: {
									title: {
										show: false,
										size: 10
									},
									legend: {
										size: 10
									}
								}
							}
					}
				})
				
				this.chart.setSymbol({ ticker: symbol })
				this.chart.setPeriod({ span: 1, type: 'day' })

				let that = this;
				const timer = setInterval(function() {
					//console.log('this.socket: ', that.socket.isConnected());
					if(!that.socket.isConnected()){
						return;
					}
					//let data = that.generateSampleData();
					
					const transferData = function(d){
						return {
						  timestamp: d.id * 1000,
						  open: d.open,
						  high: d.high,
						  low: d.low,
						  close: d.close,
						  volume: d.vol
						}
					}
					
					that.chart.setDataLoader({
						getBars: (params) => {
							//前拉和后拉不执行
							if(params.type == 'backward' || params.type == 'forward'){
								return;
							} 
							
							//console.log("getBars: ", params.type, ',   ' , params.symbol.ticker, ',  ', params.period.span + params.period.type);
							//params.callback(data)
							const type = params.period.type == 'minute' ? 'min' : params.period.type;
							const period = params.period.span + type;
							let ch = `market.${params.symbol.ticker.toLowerCase()}.kline.${period}`;
							let wsParams = {
							  "id": new Date().getTime(),
							  "req": ch,
							}
							that.socket.on(`req-${ch}`, (result) => {
							  const list = result.data.data;
							  let newList = [];
							  for(const d of list){
								  newList.push(transferData(d))
							  }
								params.callback(newList)
							});
							that.socket.send(JSON.stringify(wsParams));
							
						},
						subscribeBar: (params) => {
							//console.log("subscribeBar: ", ',   ' , params.symbol.ticker, ',  ', params.period.span + params.period.type);
							
							const type = params.period.type == 'minute' ? 'min' : params.period.type;
							const period = params.period.span + type;
							let ch = `market.${params.symbol.ticker.toLowerCase()}.kline.${period}`;
							let wsParams = {
							  "id": new Date().getTime(),
							  "sub": ch,
							}
							
							that.socket.on(`sub-${ch}`, (result) => {
								//console.log(`sub-${ch}: `, result.data);
								let d = transferData(result.data.tick);
								//d.timestamp = d.timestamp + 86400;
								
								// let max = d.close;
								// let min = max - 20;
								// d.close = Math.random() * (max - min) + min;
								params.callback(d)
							});
							that.socket.send(JSON.stringify(wsParams));
							
						},
						unsubscribeBar: (params) => {
							//console.log("unsubscribeBar: ", ',   ' , params.symbol.ticker, ',  ', params.period.span + params.period.type);
							
							const type = params.period.type == 'minute' ? 'min' : params.period.type;
							const period = params.period.span + type;
							let ch = `market.${params.symbol.ticker.toLowerCase()}.kline.${period}`;
							let wsParams = {
							  "id": new Date().getTime(),
							  "unsub": ch,
							}
							that.socket.send(JSON.stringify(wsParams));
						}
					})
					clearInterval(timer);
				}, 1000);
				// 应用主题
				//this.applyTheme('dark')
				
				
			},

			applyTheme(theme) {
				if (!this.chart) return

				const lightTheme = {
					grid: {
						color: '#EDEDED'
					},
					candle: {
						bar: {
							upColor: '#EF5350',
							downColor: '#26A69A',
							noChangeColor: '#888888'
						},
						marker: {
							upColor: '#EF5350',
							downColor: '#26A69A'
						}
					}
				}

				const darkTheme = {
					grid: {
						color: '#2B2B43'
					},
					candle: {
						bar: {
							upColor: '#F6465D',
							downColor: '#0ECB81',
							noChangeColor: '#888888'
						},
						marker: {
							upColor: '#F6465D',
							downColor: '#0ECB81'
						}
					}
				}

				this.chart.setStyles(theme === 'dark' ? darkTheme : lightTheme)
			},

			

			disposeChart() {
				if (this.chart) {
					dispose(this.chart)
					this.chart = null
				}
			}
		}
	}
</script>

<style lang="scss">
	.chart{
		background-image: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.8)), url(/static/kline_logo.png);
		background-repeat: no-repeat;
		background-position: center;
		background-size: 160rpx 160rpx;
	}
	.popup-period{
		padding: 20rpx 20rpx;
		.t1{
			font-weight: bold;
			padding: 10rpx 0 20rpx 0;
		}
		.grid{
			display: grid;
			grid-template-columns: repeat(4, 1fr);
			gap: 10rpx;
			.col{
			  text-align: center;
			  padding: 10rpx 0;
			  font-size: 24rpx;
			  border-radius: 5px;
			  border: 1rpx solid #bdc0c0;
			}
			.active{
				border: 1rpx solid #2979ff;
			}
		}
	}
	.tools{
		display: flex;
		font-size: 28rpx;
		padding: 10rpx 0;
		.period{
			display: flex;
			flex: 2;
			align-items: center;
			.col{
				width: 20%;
				text-align: center;
				margin-left: 10rpx;
				.block{
					background-color: gold;
					border-radius: 50rpx;				
					padding: 5rpx 0;
				}
			}
		}
		.indic{
			flex: 1;
			display: flex;
			align-items: center;
			justify-content: space-between;
			.col{
				width: 50%;
				text-align: center;
				.more{
					display: flex;
					align-items: center;
					justify-content: center;
				}
			}
		}
	}
	.indicator{
		height: 60rpx;
		line-height: 60rpx;
		.tabs{
			display: flex;
			align-items: center;
			.scroll-view-item{
				margin-left: 20rpx;
			}
			.active{
				color: #2979ff;
			}
		}
	}
</style>